﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace block_explorer
{
    class GBuffer
    {
        private RenderTarget2D mColorRT;
        private RenderTarget2D mNormalRT;
        private RenderTarget2D mDepthRT;

        private Effect mClearGBufferEffect;
        private RenderWorldEffect mRenderGBufferEffect;

        public Texture2D Color { get { return mColorRT; } }
        public Texture2D Normal { get { return mNormalRT; } }
        public Texture2D Depth { get { return mDepthRT; } }

        private class RenderWorldEffect : Shaders.WorldShader
        {
            private const string sEffectFilename = "Effects/RenderGBuffer";
            private const string sWorldParameterName = "World";
            private const string sViewParameterName = "View";
            private const string sProjectionParameterName = "Projection";
            private const string sTextureParameterName = "Texture";
            private const string sTechniqueName = "Render";

            private Effect mEffect;

            private EffectParameter mWorldMatrix;
            private EffectParameter mViewMatrix;
            private EffectParameter mProjectionMatrix;
            private EffectParameter mTexture;

            public override Matrix World { set { mWorldMatrix.SetValue(value); } }
            public override Matrix View { set { mViewMatrix.SetValue(value); } }
            public override Matrix Projection { set { mProjectionMatrix.SetValue(value); } }

            public override Texture Texture { set { mTexture.SetValue(value); } }

            public override EffectPassCollection Passes { get { return mEffect.CurrentTechnique.Passes; } }

            public RenderWorldEffect(GraphicsDevice device, ContentManager content)
            {
                mEffect = content.Load<Effect>(sEffectFilename);

                mWorldMatrix = mEffect.Parameters[sWorldParameterName];
                mViewMatrix = mEffect.Parameters[sViewParameterName];
                mProjectionMatrix = mEffect.Parameters[sProjectionParameterName];
                mTexture = mEffect.Parameters[sTextureParameterName];

                mEffect.CurrentTechnique = mEffect.Techniques[sTechniqueName];
            }
        }

        public GBuffer()
        {
        }

        public void Prepare(GraphicsDevice device, ContentManager content, int bufferWidth, int bufferHeight)
        {
            mColorRT = new RenderTarget2D(device, bufferWidth, bufferHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);
            mNormalRT = new RenderTarget2D(device, bufferWidth, bufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
            mDepthRT = new RenderTarget2D(device, bufferWidth, bufferHeight, false, SurfaceFormat.Single, DepthFormat.None);

            mClearGBufferEffect = content.Load<Effect>("Effects/ClearGBuffer");
            mRenderGBufferEffect = new RenderWorldEffect(device, content);
        }

        public void Fill(GraphicsDevice device, Scene scene)
        {
            device.SetRenderTargets(mColorRT, mNormalRT, mDepthRT);

            Clear();

            mRenderGBufferEffect.Projection = scene.SelectedCamera.Projection;
            mRenderGBufferEffect.View = scene.SelectedCamera.View;

            scene.Draw(mRenderGBufferEffect);
        }

        private void Clear()
        {
            mClearGBufferEffect.CurrentTechnique = mClearGBufferEffect.Techniques["Clear"];
            mClearGBufferEffect.CurrentTechnique.Passes[0].Apply();
            Util.QuadRenderComponent.Instance.Render(-1 * Vector2.One, Vector2.One);
        }
    }
}
